package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"gitee.com/thubcc/p2pgit/some"
	"gitee.com/thubcc/p2pgit/types"
	"gitee.com/thubcc/p2pgit/utils"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

var (
	argNewPack = flag.String("n", "../temp", "New Pack Dir")
)

var t = log.New(os.Stdout, "git:", 6)
var counter int

func worker(ctx context.Context, as types.SomeAsyncer, p *utils.Progress, inc, outc chan types.Hash) {
	for {
		select {
		case <-ctx.Done():
			return
		case h := <-inc:
			counter++
			as.ResolveAsync(
				ctx,
				types.HashSelect(h),
				func(s types.Selector, obj []byte, typ plumbing.ObjectType, err error) error {
					if err != nil {
						t.Println("get", err)
						return err
					}
					ghash := types.Hash(plumbing.ComputeHash(typ, obj))
					vs, err := types.Selector2Hash(s)
					if ghash != vs {
						t.Println(vs.String(), "!=", ghash.String())
					}
					outc <- ghash
					p.Done()
					counter--
					return nil
				},
			)
		}
	}
}
func read(objs []types.Hash, gs types.SomeGeter) {
	var err error
	ps := some.Geter2Puter(gs)
	as := some.Geter2Asyncer(gs)
	for k, p := range ps {
		err = p.Open(false)
		if err != nil {
			t.Println("open", k, err)
			t.Fatal()
		}
		defer p.Close()
	}
	p := utils.NewProgress("Read:", len(objs))
	p.Every = 10000
	p.Init()
	defer p.End()
	ctx, cancel := context.WithCancel(context.TODO())
	var fc = make(chan types.Hash, 30)
	var mc = make(chan types.Hash, 30)
	var mo = make(map[types.Hash]bool)
	for i := 0; i < 16; i++ {
		go worker(ctx, as, p, fc, mc)
	}
	go func() {
		for _, v := range objs {
			select {
			case <-ctx.Done():
				return
			case fc <- v:
				if counter > 30 {
					<-time.After(time.Millisecond)
					counter -= 30
				}
			}
		}
		for i := 0; len(fc) > 0 && i < 10; i++ {
			<-time.After(time.Second)
		}
		<-time.After(time.Second * 10)
		cancel()
	}()
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case rh := <-mc:
				mo[rh] = true
			}
		}
	}()
	<-ctx.Done()
	fmt.Println()
	var c int
	for _, v := range objs {
		if _, ok := mo[v]; !ok {
			fmt.Println(v.String(), c)
			c++
		}
	}
}

func main() {
	flag.Parse()

	gs, err := some.GeterFromDir(*argNewPack)
	if err != nil {
		fmt.Println("get geters from", *argNewPack, err)
		return
	}
	objs := some.Geter2Hashes(gs)
	t.Println("find done")
	read(objs, gs)
	t.Println("read done")
}
